package com.jie.server.core.selector;

import com.jie.common.ssl.constant.SslConstant;
import com.jie.server.core.ServerApplication;
import com.jie.server.core.properties.ServerProperties;
import com.jie.server.core.entity.ChannelAttachment;
import com.jie.server.core.runnable.AcceptHandlerRunnable;
import com.jie.server.core.runnable.ReadHandlerRunnable;
import com.jie.server.core.runnable.WriteHandlerRunnable;
import com.jie.server.core.ssl.entity.ServerSslMessage;
import com.jie.server.core.ssl.util.ServerSslUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.net.InetSocketAddress;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.util.Iterator;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * @author XieZhiJie
 * @date 2022/04/22 11:45
 */
public class ServerSelector {

    /**
     * 日志输出
     */
    private static final Logger LOGGER = LoggerFactory.getLogger(ServerSelector.class);

    /**
     * 服务器的ssl协议相关的信息
     * 包含了Ca证书, 服务器公钥, 服务器私钥
     */
    public static final ServerSslMessage SERVER_SSL_MESSAGE;

    static {
        // 初始化ssl
        SERVER_SSL_MESSAGE = ServerSslUtil.initServerSslMessage(SslConstant.OPEN_SSL);
    }

    public static void run() {
        if (!ServerApplication.SERVER_STATE) {
            return;
        }
        // 生成线程池
        ThreadPoolExecutor executor = new ThreadPoolExecutor(
                ServerProperties.CORE_POOL_SIZE,
                ServerProperties.MAX_POOL_SIZE,
                ServerProperties.KEEP_ALIVE_TIME,
                TimeUnit.SECONDS,
                new ArrayBlockingQueue<>(ServerProperties.QUEUE_CAPACITY),
                new ThreadPoolExecutor.CallerRunsPolicy()
        );
        try (
                // 开启一个selector
                Selector selector = Selector.open();
                // 开启一个服务端socket的channel, 但还未绑定端口号
                ServerSocketChannel serverSocketChannel = ServerSocketChannel.open()
        ) {
            // 注册一个监听channel
            serverSocketChannel.socket().bind(new InetSocketAddress(ServerProperties.PORT));
            serverSocketChannel.configureBlocking(false);
            serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);

            LOGGER.info("服务启动成功!");

            selector(selector, executor);
        } catch (Exception e) {
            LOGGER.error(e.getMessage());
        }
        LOGGER.info("服务结束!");
    }

    /**
     * 服务端用于选择事件并处理的方法
     * @param selector   选择器
     * @param executor   线程池
     * @throws Exception Exception
     */
    private static void selector(Selector selector, ThreadPoolExecutor executor) throws Exception {
        while (ServerApplication.SERVER_STATE) {
            // 如果太大一个数, 会导致key事件被改变后但没有重新select导致没有监听到
            // 同时也不能直接阻塞在这里, 因此设置为1s, 超过1s没有事件立刻返回重新监听事件
            if (selector.select(1) == 0) {
                continue;
            }
            Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
            while (iterator.hasNext()) {
                SelectionKey key = iterator.next();
                ChannelAttachment attachment = (ChannelAttachment) key.attachment();
                if (key.isAcceptable()) {
                    // 这里本来有idea警告, 告诉我需要关闭这个channel, 但被我忽略了
                    ServerSocketChannel serverSocketChannel = (ServerSocketChannel) key.channel();
                    executor.execute(new AcceptHandlerRunnable(key, serverSocketChannel.accept()));
                } else if (key.isReadable()) {
                    // 消除读事件的监听, 处理读事件
                    attachment.interestOps(key, key.interestOps() & ~SelectionKey.OP_READ);
                    executor.execute(new ReadHandlerRunnable(key));
                } else if (key.isWritable()) {
                    // 消除写事件的监听, 处理写事件
                    attachment.interestOps(key, key.interestOps() & ~SelectionKey.OP_WRITE);
                    executor.execute(new WriteHandlerRunnable(key));
                }
                iterator.remove();
            }
        }
    }

}
